use std::any::Any;

use crate::{channel::tcp::prelude::NrWriter, cli::conf::ConfCliPlus};
use crate::channel::http::prelude::NrReq;

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum FunctionType {
    UpdateCli,
    RemoveCli,
    UpdateUser,
    RemoveUser,
    UpdateReq,
    CliRes,
}

pub trait Function: Send {
    
    fn kind(&self) -> FunctionType;

    fn as_any(&self) -> &dyn Any;

}

#[derive(Debug)]
pub struct CliFunction {
    kind: FunctionType,
    pub cid: i64,
    pub conf: Option<ConfCliPlus>,
    pub stream: Option<NrWriter>,
}

impl Function for CliFunction {
    
    fn kind(&self) -> FunctionType {
        self.kind
    }

    fn as_any(&self) -> &dyn Any {
        self
    }

}

impl CliFunction {

    pub fn new_update(cid: i64, conf: Option<ConfCliPlus>, stream: Option<NrWriter>) -> Self {
        Self { kind: FunctionType::UpdateCli, cid, conf, stream }
    }

    pub fn new_remove(cid: i64) -> Self {
        Self { kind: FunctionType::RemoveCli, cid, conf: None, stream: None }
    }
    
}

#[derive(Debug)]
pub struct UserFunction {
    kind: FunctionType,
    pub uid: i64,
    pub stream: Option<NrWriter>,
}

impl<'a> Function for UserFunction {
    
    fn kind(&self) -> FunctionType {
        self.kind
    }

    fn as_any(&self) -> &dyn Any {
        self
    }

}

impl UserFunction {

    pub fn new_update(uid: i64, stream: NrWriter) -> Self {
        Self { kind: FunctionType::UpdateUser, uid, stream: Some(stream) }
    }

    pub fn new_remove(uid: i64) -> Self {
        Self { kind: FunctionType::RemoveUser, uid, stream: None }
    }
    
}

#[derive(Debug)]
pub struct ReqFunction {
    kind: FunctionType,
    pub pid: i64,
    pub uid: Option<i64>,
    pub req: Option<NrReq>,
}

impl Function for ReqFunction {
    
    fn kind(&self) -> FunctionType {
        self.kind
    }

    fn as_any(&self) -> &dyn Any {
        self
    }

}

impl ReqFunction {

    pub fn new_update(pid: i64, uid: i64, req: NrReq) -> Self {
        Self { kind: FunctionType::UpdateReq, pid, uid: Some(uid), req: Some(req) }
    }
    
}

pub struct ResFunction {
    kind: FunctionType,
    pub pid: i64,
    pub data: Vec<u8>,
}

impl Function for ResFunction {
    
    fn kind(&self) -> FunctionType {
        self.kind
    }

    fn as_any(&self) -> &dyn Any {
        self
    }

}

impl ResFunction {

    pub fn new(pid: i64, data: Vec<u8>) -> Self {
        Self { kind: FunctionType::CliRes, pid, data }
    }
    
}